package com.shlh.saas.service.impl;


import com.microsoft.aad.msal4j.ClientCredentialFactory;
import com.microsoft.aad.msal4j.ClientCredentialParameters;
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.aad.msal4j.IAuthenticationResult;
import com.microsoft.graph.authentication.IAuthenticationProvider;
import com.microsoft.graph.models.*;
import com.microsoft.graph.requests.AttachmentCollectionPage;
import com.microsoft.graph.requests.AttachmentCollectionResponse;
import com.microsoft.graph.requests.GraphServiceClient;
import com.shlh.saas.service.OutlookOAuthService;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import okhttp3.Request;

import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Service
public class OutlookOAuthServiceImpl implements OutlookOAuthService {
    private final RestTemplate restTemplate = new RestTemplate();

    public Map<String, Object> startDeviceCode(String tenantId, String clientId) {
        String url = "https://login.microsoftonline.com/" + (tenantId == null || tenantId.isEmpty() ? "common" : tenantId) + "/oauth2/v2.0/devicecode";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        String body = "client_id=" + clientId + "&scope=" + encode("offline_access openid profile https://graph.microsoft.com/Mail.Send");
        HttpEntity<String> req = new HttpEntity<>(body, headers);
        ResponseEntity<Map<String, Object>> resp = restTemplate.exchange(url, HttpMethod.POST, req, new org.springframework.core.ParameterizedTypeReference<Map<String, Object>>() {});
        return resp.getBody();
    }

    public Map<String, Object> pollDeviceToken(String tenantId, String clientId, String deviceCode) {
        String url = "https://login.microsoftonline.com/" + (tenantId == null || tenantId.isEmpty() ? "common" : tenantId) + "/oauth2/v2.0/token";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        String body = "grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id=" + clientId + "&device_code=" + deviceCode;
        HttpEntity<String> req = new HttpEntity<>(body, headers);
        ResponseEntity<Map<String, Object>> resp = restTemplate.exchange(url, HttpMethod.POST, req, new org.springframework.core.ParameterizedTypeReference<Map<String, Object>>() {});
        return resp.getBody();
    }

    public Map<String, Object> refreshToken(String tenantId, String clientId, String refreshToken) {
        String url = "https://login.microsoftonline.com/" + (tenantId == null || tenantId.isEmpty() ? "common" : tenantId) + "/oauth2/v2.0/token";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        String body = "grant_type=refresh_token&client_id=" + clientId + "&refresh_token=" + refreshToken;
        HttpEntity<String> req = new HttpEntity<>(body, headers);
        ResponseEntity<Map<String, Object>> resp = restTemplate.exchange(url, HttpMethod.POST, req, new org.springframework.core.ParameterizedTypeReference<Map<String, Object>>() {});
        return resp.getBody();
    }

    public boolean testGraphConnection(String email, String clientId, String tenantId, String clientSecret, String redirectUri) {
        // Lightweight parameter sanity check; real connectivity can be verified via getMe(accessToken)
        return email != null && !email.isEmpty() && clientId != null && !clientId.isEmpty();
    }

    public LocalDateTime toExpiry(int expiresInSeconds) {
        Instant now = Instant.now();
        return LocalDateTime.ofInstant(now.plusSeconds(expiresInSeconds - 60), ZoneOffset.UTC);
    }

    private String encode(String s) {
        return s.replace(" ", "+");
    }

    public boolean getMe(String accessToken) {
        String url = "https://graph.microsoft.com/v1.0/me";
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(accessToken);
        HttpEntity<Void> req = new HttpEntity<>(headers);
        ResponseEntity<String> resp = restTemplate.exchange(url, HttpMethod.GET, req, String.class);
        return resp.getStatusCode().is2xxSuccessful();
    }

    public boolean sendMailViaGraph(String accessToken, String fromEmail, String subject, String htmlContent, String toEmail) {
        String url = "https://graph.microsoft.com/v1.0/me/sendMail";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(accessToken);
        Map<String, Object> payload = new HashMap<>();
        Map<String, Object> message = new HashMap<>();
        Map<String, Object> body = new HashMap<>();
        body.put("contentType", "HTML");
        body.put("content", htmlContent);
        message.put("subject", subject);
        message.put("body", body);
        message.put("toRecipients", new Object[]{ new HashMap<String, Object>() {{ put("emailAddress", new HashMap<String, Object>() {{ put("address", toEmail); }}); }} });
        payload.put("message", message);
        payload.put("saveToSentItems", true);
        HttpEntity<Map<String, Object>> req = new HttpEntity<>(payload, headers);
        ResponseEntity<String> resp = restTemplate.postForEntity(url, req, String.class);
        return resp.getStatusCode().is2xxSuccessful();
    }

    public boolean sendMailViaGraph(String accessToken, String fromEmail, String subject, String htmlContent, String toEmail, List<Map<String, Object>> attachments) {
        String url = "https://graph.microsoft.com/v1.0/me/sendMail";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(accessToken);
        Map<String, Object> payload = new HashMap<>();
        Map<String, Object> message = new HashMap<>();
        Map<String, Object> body = new HashMap<>();
        body.put("contentType", "HTML");
        body.put("content", htmlContent);
        message.put("subject", subject);
        message.put("body", body);
        message.put("toRecipients", new Object[]{ new HashMap<String, Object>() {{ put("emailAddress", new HashMap<String, Object>() {{ put("address", toEmail); }}); }} });
        if (attachments != null && !attachments.isEmpty()) {
            message.put("attachments", attachments);
        }
        payload.put("message", message);
        payload.put("saveToSentItems", true);
        HttpEntity<Map<String, Object>> req = new HttpEntity<>(payload, headers);
        ResponseEntity<String> resp = restTemplate.postForEntity(url, req, String.class);
        return resp.getStatusCode().is2xxSuccessful();
    }

    @Override
    public boolean sendEmailInfo(String email, String clientId, String clientSecret, String tenantId, String subject, String content, String to) {
        String accessToken;
        try {
            accessToken = getAccessToken(clientId, clientSecret, tenantId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 2. 创建 Graph 客户端
        GraphServiceClient<okhttp3.Request> graphClient = createGraphClient(accessToken);


        Message message = new Message();
        message.subject = subject;

        // 创建邮件正文对象
        ItemBody body = new ItemBody();
        body.contentType = BodyType.TEXT; // 设置正文类型为纯文本
        body.content = content;
        message.body = body;

        // 创建收件人
        Recipient recipient = new Recipient();
        EmailAddress emailAddress = new EmailAddress();
        emailAddress.address = to; // 替换为实际收件人
        recipient.emailAddress = emailAddress;

        message.toRecipients = Collections.singletonList(recipient);

        // 4. 发送邮件
        sendEmail(graphClient, email, message);

        return true;
    }

    @Override
    public boolean sendEmailInfoWithAttachments(String email, String clientId, String clientSecret,
                                                String tenantId, String subject, String content,
                                                String to, List<Map<String, Object>> graphAttachments) {
        String accessToken;
        try {
            accessToken = getAccessToken(clientId, clientSecret, tenantId);
        } catch (Exception e) {
            throw new RuntimeException("获取访问令牌失败", e);
        }

        // 创建 Graph 客户端
        GraphServiceClient<Request> graphClient = createGraphClient(accessToken);

        // 创建邮件对象
        Message message = createEmailMessage(subject, content, to);

        // 添加附件
        addAttachmentsToMessage(message, graphAttachments);

        // 发送邮件
        sendEmail(graphClient, email, message);

        return true;
    }

    private Message createEmailMessage(String subject, String content, String to) {
        Message message = new Message();
        message.subject = subject;

        // 创建邮件正文
        ItemBody body = new ItemBody();
        body.contentType = BodyType.TEXT;
        body.content = content;
        message.body = body;

        // 创建收件人
        Recipient recipient = new Recipient();
        EmailAddress emailAddress = new EmailAddress();
        emailAddress.address = to;
        recipient.emailAddress = emailAddress;

        message.toRecipients = Collections.singletonList(recipient);

        return message;
    }

    private void addAttachmentsToMessage(Message message, List<Map<String, Object>> attachments) {
        if (attachments == null || attachments.isEmpty()) {
            return;
        }

        // 创建附件列表
        List<Attachment> attachmentList = new ArrayList<>();

        for (Map<String, Object> attachmentInfo : attachments) {
            FileAttachment attachment = new FileAttachment();
            attachment.name = (String) attachmentInfo.get("name");
            attachment.contentType = (String) attachmentInfo.get("contentType");
            attachment.oDataType = "#microsoft.graph.fileAttachment";

            // 处理内容数据 - 支持字符串和字节数组
            Object content = attachmentInfo.get("contentBytes");
            if (content instanceof byte[]) {
                // 已经是字节数组
                attachment.contentBytes = (byte[]) content;
            } else if (content instanceof String) {
                // 字符串转换为字节数组
                attachment.contentBytes = ((String) content).getBytes(StandardCharsets.UTF_8);
            } else {
                throw new IllegalArgumentException("不支持的附件内容类型: " + content.getClass());
            }

            attachmentList.add(attachment);
        }

        // 创建 AttachmentCollectionResponse
        AttachmentCollectionResponse response = new AttachmentCollectionResponse();
        response.value = attachmentList;

        // 创建 AttachmentCollectionPage
        AttachmentCollectionPage page = new AttachmentCollectionPage(response, null);

        // 设置到消息对象
        message.attachments = page;
    }


    private String getAccessToken(String clientId, String clientSecret, String tenantId) throws Exception {
        // 使用新的方式创建客户端凭证
        ConfidentialClientApplication app = ConfidentialClientApplication.builder(
                        clientId,
                        ClientCredentialFactory.createFromSecret(clientSecret)) // 直接使用客户端密钥字符串
                .authority("https://login.microsoftonline.com/" + tenantId + "/")
                .build();

        ClientCredentialParameters parameters = ClientCredentialParameters.builder(
                        Collections.singleton("https://graph.microsoft.com/.default"))
                .build();

        CompletableFuture<IAuthenticationResult> future = app.acquireToken(parameters);
        IAuthenticationResult result = future.get();

        System.out.println("获取访问令牌成功，Token 类型: " + result);
        System.out.println("令牌过期时间: " + result.expiresOnDate());

        return result.accessToken();
    }

    private static GraphServiceClient<Request> createGraphClient(String accessToken) {
        IAuthenticationProvider authProvider = new IAuthenticationProvider() {
            @Override
            public CompletableFuture<String> getAuthorizationTokenAsync(URL requestUrl) {
                return CompletableFuture.completedFuture(accessToken);
            }
        };

        return GraphServiceClient.builder()
                .authenticationProvider(authProvider)
                .buildClient();
    }

    private void sendEmail(GraphServiceClient<Request> graphClient, String userEmail, Message message) {
        UserSendMailParameterSet params = UserSendMailParameterSet.newBuilder()
                .withMessage(message)
                .withSaveToSentItems(true)
                .build();

        graphClient.users(userEmail)
                .sendMail(params)
                .buildRequest()
                .post();

        System.out.println("邮件发送请求成功提交");
    }
}
